﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using Kinect.Framework.Lib;
using System.Windows.Media;
using System.Windows.Media.Animation;
using Kinect.Framework.Interfaces;

namespace Kinect.Framework.Controls
{
    public class KinectScrollViewer : ScrollViewer, IKinectElement
    {
        #region Events GotKinectFocused
        public delegate void GotKinectFocusedEventHandler(object s, PayloadEventArgs<IKinectElement> e);
        public event GotKinectFocusedEventHandler GotKinectFocused;
        public void OnGotKinectFocused(IKinectElement value)
        {
            if (GotKinectFocused != null)
                GotKinectFocused(this, new PayloadEventArgs<IKinectElement>(value));
        }
        #endregion

        #region Events LostKinectFocused
        public delegate void LostKinectFocusedEventHandler(object s, PayloadEventArgs<IKinectElement> e);
        public event LostKinectFocusedEventHandler LostKinectFocused;
        public void OnLostKinectFocused(IKinectElement value)
        {
            if (LostKinectFocused != null)
                LostKinectFocused(this, new PayloadEventArgs<IKinectElement>(value));
        }
        #endregion

        #region VerticalOffset
        /// <summary>
        /// VerticalOffset Attached Dependency Property

        /// </summary>
        public static readonly DependencyProperty VerticalOffsetAnimatedProperty = DependencyProperty.RegisterAttached("VerticalOffsetAnimated", typeof(double), typeof(KinectScrollViewer), new FrameworkPropertyMetadata((double)0.0, new PropertyChangedCallback(OnVerticalOffsetAnimatedChanged)));

        /// <summary>
        /// Gets the VerticalOffset property.  This dependency property 
        /// indicates ....
        /// </summary>
        public static double GetVerticalOffsetAnimated(DependencyObject d)
        {
            return (double)d.GetValue(VerticalOffsetAnimatedProperty);
        }
        /// <summary>
        /// Sets the VerticalOffset property.  This dependency property 
        /// indicates ....
        /// </summary>
        public static void SetVerticalOffsetAnimated(DependencyObject d, double value)
        {
            d.SetValue(VerticalOffsetAnimatedProperty, value);
        }/// <summary>
        /// Handles changes to the VerticalOffset property.
        /// </summary>
        private static void OnVerticalOffsetAnimatedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var viewer = (ScrollViewer)d;
            viewer.ScrollToVerticalOffset((double)e.NewValue);
        }
        #endregion

        #region HorizontalOffset
        /// <summary>
        /// HorizontalOffset Attached Dependency Property

        /// </summary>
        public static readonly DependencyProperty HorizontalOffsetAnimatedProperty = DependencyProperty.RegisterAttached("HorizontalOffsetAnimated", typeof(double), typeof(KinectScrollViewer), new FrameworkPropertyMetadata((double)0.0, new PropertyChangedCallback(OnHorizontalOffsetAnimatedChanged)));

        /// <summary>
        /// Gets the HorizontalOffset property.  This dependency property 
        /// indicates ....
        /// </summary>
        public static double GetHorizontalOffsetAnimated(DependencyObject d)
        {
            return (double)d.GetValue(HorizontalOffsetAnimatedProperty);
        }
        /// <summary>
        /// Sets the HorizontalOffset property.  This dependency property 
        /// indicates ....
        /// </summary>
        public static void SetHorizontalOffsetAnimated(DependencyObject d, double value)
        {
            d.SetValue(HorizontalOffsetAnimatedProperty, value);
        }/// <summary>
        /// Handles changes to the HorizontalOffset property.
        /// </summary>
        private static void OnHorizontalOffsetAnimatedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var viewer = (ScrollViewer)d;
            viewer.ScrollToHorizontalOffset((double)e.NewValue);
        }
        #endregion

        #region ScrollingZone
        /// <summary>
        /// The ScrollingZone Dependency
        /// </summary>
        public static readonly DependencyProperty ScrollingZoneProperty = DependencyProperty.RegisterAttached("ScrollingZone", typeof(double), typeof(KinectScrollViewer), new FrameworkPropertyMetadata((double)0.2, new PropertyChangedCallback(OnScrollingZoneChanged)));
        /// <summary>
        /// The Header of your item
        /// </summary>
        public double ScrollingZone
        {
            get { return (double)GetValue(KinectScrollViewer.ScrollingZoneProperty); }
            set
            {
                SetValue(KinectScrollViewer.ScrollingZoneProperty, value);
            }
        }
        /// <summary>
        /// Handles changes to the ScrollingZone property.
        /// </summary>
        private static void OnScrollingZoneChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
        }
        #endregion

        #region ScrollDistance
        /// <summary>
        /// The ScrollDistance Dependency
        /// </summary>
        public static readonly DependencyProperty ScrollDistanceProperty = DependencyProperty.RegisterAttached("ScrollDistance", typeof(int), typeof(KinectScrollViewer), new FrameworkPropertyMetadata((int)200.0, new PropertyChangedCallback(OnScrollDistanceChanged)));
        /// <summary>
        /// The Header of your item
        /// </summary>
        public int ScrollDistance
        {
            get { return (int)GetValue(KinectScrollViewer.ScrollDistanceProperty); }
            set
            {
                SetValue(KinectScrollViewer.ScrollDistanceProperty, value);
            }
        }
        /// <summary>
        /// Handles changes to the ScrollDistance property.
        /// </summary>
        private static void OnScrollDistanceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
        }
        #endregion

        #region ScrollingStep
        /// <summary>
        /// ScrollingStep Attached Dependency Property

        /// </summary>
        public static readonly DependencyProperty ScrollingStepAnimatedProperty = DependencyProperty.RegisterAttached("ScrollingStepAnimated", typeof(double), typeof(KinectScrollViewer), new FrameworkPropertyMetadata((double)0.0, new PropertyChangedCallback(OnScrollingStepAnimatedChanged)));

        /// <summary>
        /// Gets the ScrollingStep property.  This dependency property 
        /// indicates ....
        /// </summary>
        public static double GetScrollingStepAnimated(DependencyObject d)
        {
            return (double)d.GetValue(ScrollingStepAnimatedProperty);
        }
        /// <summary>
        /// Sets the ScrollingStep property.  This dependency property 
        /// indicates ....
        /// </summary>
        public static void SetScrollingStepAnimated(DependencyObject d, double value)
        {
            d.SetValue(ScrollingStepAnimatedProperty, value);
        }/// <summary>
        /// Handles changes to the ScrollingStep property.
        /// </summary>
        private static void OnScrollingStepAnimatedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var viewer = (ScrollViewer)d;
        }
        #endregion

        #region AnimationDuration
        /// <summary>
        /// The AnimationDuration Dependency
        /// </summary>
        public static readonly DependencyProperty AnimationDurationProperty = DependencyProperty.RegisterAttached("AnimationDuration", typeof(int), typeof(KinectScrollViewer), new FrameworkPropertyMetadata((int)200.0, new PropertyChangedCallback(OnAnimationDurationChanged)));
        /// <summary>
        /// The Header of your item
        /// </summary>
        public int AnimationDuration
        {
            get { return (int)GetValue(KinectScrollViewer.AnimationDurationProperty); }
            set
            {
                SetValue(KinectScrollViewer.AnimationDurationProperty, value);
            }
        }
        /// <summary>
        /// Handles changes to the AnimationDuration property.
        /// </summary>
        private static void OnAnimationDurationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
        }
        #endregion

        #region ScrollOnWindow
        /// <summary>
        /// The ScrollOnWindow Dependency
        /// </summary>
        public static readonly DependencyProperty ScrollOnWindowProperty = DependencyProperty.RegisterAttached("ScrollOnWindow", typeof(bool), typeof(KinectScrollViewer), new FrameworkPropertyMetadata((bool)false, new PropertyChangedCallback(OnScrollOnWindowChanged)));
        /// <summary>
        /// The Header of your item
        /// </summary>
        public bool ScrollOnWindow
        {
            get { return (bool)GetValue(KinectScrollViewer.ScrollOnWindowProperty); }
            set
            {
                SetValue(KinectScrollViewer.ScrollOnWindowProperty, value);
            }
        }
        /// <summary>
        /// Handles changes to the ScrollOnWindow property.
        /// </summary>
        private static void OnScrollOnWindowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            //((KinectListBox)d).UpdateItemsLayout();
        }
        #endregion

        public bool isScrolling { get; set; }

        #region IKinectElement Members

        public void GotKinectFocus(ManagedNite.XnMPoint point, ManagedNite.XnMPoint realPoint, UIElement OriginalSource)
        {
            OnGotKinectFocused(this);
        }

        public void LostKinectFocus()
        {
            OnLostKinectFocused(this);
        }

        #endregion

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            ScrollViewer.SetHorizontalScrollBarVisibility(this, ScrollBarVisibility.Hidden);
            ScrollViewer.SetVerticalScrollBarVisibility(this, ScrollBarVisibility.Hidden);
            if (!KinectManipulation.IsDesignTime)
                KinectWindow.GestureController(this).ManipulationDetected += new Controller.KinectManipulationController.ManipulationDetectedEventHandler(OnManipulationDetected);
        }

        void OnManipulationDetected(object s, PayloadEventArgs<KinectManipulationDelta> e)
        {
            AttachScroll(new Point(e.Payload.Position.X, e.Payload.Position.Y), (FrameworkElement)this);
        }

        private void AttachScroll(Point point, FrameworkElement control)
        {
            if (KinectWindow.CurrentWindow(this) == null)
                return;
            if (ScrollOnWindow)
                control = KinectWindow.CurrentWindow(this);
            var pointTransform = control.TransformToVisual(KinectWindow.CurrentWindow(this));
            var hitPoint = new Point()
            {
                X = point.X - ((MatrixTransform)(pointTransform)).Value.OffsetX,
                Y = point.Y - ((MatrixTransform)(pointTransform)).Value.OffsetY
            };
            if (control.InputHitTest(hitPoint) != null)
            {
                var zoneTopTo = control.ActualHeight * ScrollingZone;
                var zoneBottomFrom = control.ActualHeight - control.ActualHeight * ScrollingZone;
                if (point.Y < zoneTopTo)
                    DescreaseListSelectedIndex();
                if (zoneBottomFrom < point.Y)
                    IncreaseListSelectedIndex();
            }

        }
        public void IncreaseListSelectedIndex()
        {
            if (!isScrolling)
            {
                SmoothScrolling(Convert.ToInt32(Math.Floor(VerticalOffset + ScrollDistance)));
            }
        }

        public void DescreaseListSelectedIndex()
        {
            if (!isScrolling)
            {
                SmoothScrolling(Convert.ToInt32(Math.Floor(VerticalOffset - ScrollDistance)));
            }
        }

        private void SmoothScrolling(int to)
        {
            if (isScrolling)
                return;
            isScrolling = true;
            int from = Convert.ToInt32(VerticalOffset);
            var storyboard = new Storyboard();

            DoubleAnimation oScrollAnimation = new DoubleAnimation();
            oScrollAnimation.Completed += (se, ev) =>
            {
                isScrolling = false;
            };
            oScrollAnimation.From = from;
            oScrollAnimation.To = to;
            oScrollAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(AnimationDuration));
            this.BeginAnimation(KinectScrollViewer.VerticalOffsetAnimatedProperty, oScrollAnimation);
        }

        
    }
}
